package com.aracky.common.annotation.dataconvert;

import com.aracky.common.annotation.DataDeal;
import com.aracky.common.annotation.dataconvert.dict.DataDict;
import com.aracky.common.annotation.dataconvert.dict.DictDto;
import com.aracky.common.annotation.dataconvert.user.DataUser;
import com.aracky.common.annotation.dataconvert.user.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author zengzhiping
 * @ClassName DataParamUtil
 * @Description 映射数据字段获取工具类 ---------获取所有属性值被对应注解修饰的字段和需要赋值的对象
 * @createTime 2022年06月14日
 */
@Slf4j
public class DataParamUtil {
    /**
     * 用户key
     */
    public static final String USER_KEY = "USER_KEY";
    /**
     * 字典key
     */
    public static final String DICT_KEY = "DICT_KEY";

    /**
     * 现在实现了用户和字典，后期有其他的继续往后追加
     *
     * @param obj
     * @return
     * @throws IllegalAccessException
     */
    public static Map<String, List> getMapping(Object obj) throws IllegalAccessException {
        Map<String, List> resultMap = new HashMap<>(2);

        //用户信息
        List<UserDto> userDtoList = new ArrayList<>();
        //字典信息
        List<DictDto> dictDtoList = new ArrayList<>();

        //需要判断是否为集合,集合包含List<List<User>>的情况
        loopMapping(obj, userDtoList, dictDtoList);
        resultMap.put(USER_KEY, userDtoList);
        resultMap.put(DICT_KEY, dictDtoList);
        return resultMap;
    }

    private static void loopMapping(Object obj, List<UserDto> userDtoList, List<DictDto> dictDtoList) throws IllegalAccessException {
        Field[] fields;
        if (obj instanceof Collection) {
            Collection olist = ((Collection) obj);
            if (olist == null || olist.size() == 0) {
                return;
            }
            for (Object temp : olist) {
                loopMapping(temp, userDtoList, dictDtoList);
            }
        } else {
            //单对象
            fields = obj.getClass().getDeclaredFields();
            loopFields(fields, obj, "", userDtoList, dictDtoList);
        }
    }

    /**
     * 增加一个前缀，表示属性值中包含集合其他属性的情况
     * 反射性能消耗资源大，多个注解的获取尽量在一次循环中获取（设置值也是一样）
     * 除了获取对应注解的值，也需要判断当前属性类型是否为对象或者是集合，分别进行处理
     *
     * @param fields
     * @param obj
     * @param prefix
     * @param userDtoList
     * @param dictDtoList
     * @throws IllegalAccessException
     */
    private static void loopFields(Field[] fields, Object obj, String prefix,
                                   List<UserDto> userDtoList, List<DictDto> dictDtoList) throws IllegalAccessException {

        for (Field field : fields) {
            //去掉安全检查
            field.setAccessible(true);

            if (field.isAnnotationPresent(DataUser.class)) {
                //获取用户信息
                getUserMapping(obj, field, prefix, userDtoList);
                continue;
            }

            if (field.isAnnotationPresent(DataDict.class)) {
                //获取字典信息
                getDictMapping(field, prefix, dictDtoList);
                continue;
            }
            //判断该字段是否为list
            if (Collection.class.isAssignableFrom(field.getType())) {
                //是集合，又需要循环获取每个字段
                Collection olist = (Collection) field.get(obj);
                if (olist == null || olist.size() == 0) {
                    continue;
                }
                loopList(prefix, userDtoList, dictDtoList, field, olist);
                continue;
            }

            //属于对象--包含当前包下的数据--写死类型
            if (isExistsModel(field.getType().toString())) {
                Class clazz = field.getClass();
                Field[] declaredFields = clazz.getDeclaredFields();
                prefix = StringUtils.isNotBlank(prefix) ? prefix + "." + field.getName() : field.getName();
                loopFields(declaredFields, obj, prefix, userDtoList, dictDtoList);
            }
        }

    }

    /**
     * 循环集合
     *
     * @param prefix
     * @param userDtoList
     * @param dictDtoList
     * @param field
     * @param olist
     * @throws IllegalAccessException
     */
    private static void loopList(String prefix, List<UserDto> userDtoList, List<DictDto> dictDtoList, Field field, Collection olist)
            throws IllegalAccessException {
        for (Object temp : olist) {
            Class<?> aClass = temp.getClass();
            if (!isExistsModel(aClass.getName())) {
                break;
            }
            Field[] tempFields = aClass.getDeclaredFields();
            String tempPrefix = StringUtils.isNotBlank(prefix) ? prefix + "." + field.getName() : field.getName();
            //循环获取字段值
            loopFields(tempFields, temp, tempPrefix, userDtoList, dictDtoList);
        }
    }

    /**
     * 判断是否存在当前包下得对象
     *
     * @param value
     * @return
     */
    public static boolean isExistsModel(String value) {
        if (value.contains("com.aracky.*.model")) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }


    /**
     * 获取字典映射信息
     *
     * @param field
     * @param dictDtoList
     * @throws IllegalAccessException
     */
    private static void getDictMapping(Field field, String prefix, List<DictDto> dictDtoList) {
        DataDict dataDict = field.getAnnotation(DataDict.class);
        String curName = StringUtils.isNotBlank(prefix) ? prefix + "." + field.getName() : field.getName();
        dictDtoList.add(new DictDto(dataDict.dictCode(), curName, dataDict.dictField()));
    }


    /**
     * 获取用户映射信息
     *
     * @param obj
     * @param field
     * @param list
     * @throws IllegalAccessException
     */
    private static void getUserMapping(Object obj, Field field, String prefix,
                                       List<UserDto> list) throws IllegalAccessException {
        DataUser dataUser = field.getAnnotation(DataUser.class);
        Object preValue = field.get(obj);
        if (Objects.isNull(preValue)) {
            return;
        }
        String curName = StringUtils.isNotBlank(prefix) ? prefix + "." + field.getName() : field.getName();
        list.add(new UserDto(String.valueOf(preValue), curName, dataUser.userField()));
    }


}
